/*
 * Core functions for Mine game
 * @author Gerald <gera2ld@163.com>
 */
function MineGame(options){
	var self=this;
	self.options=options;
	self.boxes=options.container;
	self.boxes.classList.add('mine-game');
	// 剩余的空格数，-1表示游戏未开始
	self.blanks=-1;
	self.total=0;
	self.control={
		setLevel:self.setLevel.bind(self),
		restart:self.restart.bind(self),
	};
	self.setLevel(options.level||0);
}
MineGame.prototype={
	levels:[
		{width:10,height:10,mines:10},
		{width:16,height:16,mines:40},
		{width:30,height:16,mines:99},
	],
	setLevel:function(l) {
		this.level=l;
		this.level_data=this.levels[l];
		this.restart();
	},
	callback:function(cmd,args) {
		var f=this.options['cb_'+cmd];
		if(f) f.call(this,args);
	},
	click:function(e){
		var self=this;
		if(self.blanks) {
			var i=Array.prototype.indexOf.call(self.items,e.target);
			if(e.button==2) {
				// 鼠标右键
				if(self.blanks<0) return;
				if(!self.data[i].open) {
					if(self.data[i].marked=!self.data[i].marked) {
						self.items[i].classList.add('mine');
						self.mine_left--;
					} else {
						self.items[i].classList.remove('mine');
						self.mine_left++;
					}
					self.callback('mine_left',self.mine_left);
				}
			} else {
				if(self.blanks<0) self.newGame(i);
				if(self.data[i].open) {
					if(self.validate(i)) self.spread(i);
					else self.spark(i);
				} else self.open(i);
			}
		}
	},
	getSurroundings:function(o) {
		// 返回第i个格子周围一圈的格子序号，不包括自己
		function getRowItems(i) {
			// 返回以i为中心的i所在行的3个序号
			if(i!=o) s.push(i);
			if(i%width) s.push(i-1);
			if((i+1)%width) s.push(i+1);
		}
		var s=[],width=this.level_data.width;
		if(o>=width) getRowItems(o-width);
		getRowItems(o);
		if(o+width<this.total) getRowItems(o+width);
		return s;
	},
	newGame:function(current){
		// 以所选格子为中心的3*3的范围内都不能有雷
		var t=this,mine_left=t.level_data.mines,
				cell_left=t.total-9,
				row=Math.floor(current/t.level_data.width),
				col=current%t.level_data.width,
				i,item,exclude;
		t.blanks=t.total-t.level_data.mines;
		t.data=[];
		// 如果所选格子靠边或靠角则需要排除的格子会少一些
		// 用i来计数，靠2条边即靠角
		i=0;
		if(!row||row==t.level_data.height-1) i++;
		if(!col||col==t.level_data.width-1) i++;
		if(i--) {
			// 靠一条边时，有3个被排除的格子不存在，加回来
			cell_left+=3;
			if(i) {
				// 靠角时，有5个格子不存在，则再加个2
				cell_left+=2;
			}
		}
		// 生成雷的分布表
		for(i=0;i<t.total;i++) {
			// 相隔一行一列以内则排除
			exclude=Math.abs(Math.floor(i/t.level_data.width)-row)<=1
						&&Math.abs(i%t.level_data.width-col)<=1;
			item={
				mine:!exclude&&mine_left/cell_left>Math.random(),
				num:0,
			};
			t.data.push(item);
			if(item.mine) mine_left--;
			if(!exclude) cell_left--;
		}
		// 更新所有格子中的数字
		for(i=0;i<t.total;i++) if(t.data[i].mine)
			t.getSurroundings(i).forEach(function(i){t.data[i].num++;});
		t.callback('start');
		t.startTimer();
	},
	clearTimer:function(cb){
		if(this.timer) {
			clearInterval(this.timer);
			this.timer=null;
		}
		if(cb) this.callback('timer',0);
	},
	startTimer:function(){
		var time=0,t=this;
		t.clearTimer(true);
		t.timer=setInterval(function(){
			time++;
			t.callback('timer',time);
		},1000);
	},
	restart:function(){
		this.total=this.level_data.width*this.level_data.height;
		// 生成游戏界面
		var b=[],i,j;
		for(i=0;i<this.level_data.height;i++) {
			for(j=0;j<this.level_data.width;j++)
				b.push('<span class=close></span>');
			b.push('<br>');
		}
		this.boxes.innerHTML=b.join('');
		this.items=this.boxes.querySelectorAll('span');
		this.mine_left=this.level_data.mines;
		this.callback('mine_left',this.mine_left);
		this.blanks=-1;
		this.clearTimer(true);
		this.callback('ready');
	},
	validate:function(i){
		var wrong=0,r=0,t=this;
		if(t.data[i].open) {
			r=1;
			t.getSurroundings(i).forEach(function(i){
				if(!t.data[i].mine&&t.data[i].marked)	// 标错雷了
					wrong=1;
				if(t.data[i].mine^t.data[i].marked) r=0;
			});
			if(wrong) t.lose(-1);
		}
		return r;
	},
	spread:function(i){
		// 第i个格子为空，则翻开周围的格子
		this.getSurroundings(i).forEach(this.open.bind(this));
	},
	spark:function(i){
		// 第i个格子为数字，且周围有未标记的雷，则闪烁一下
		var t=this,on=false;
		function spark() {
			on=!on;
			t.getSurroundings(i).forEach(function(i){
				if(!t.data[i].open&&!t.data[i].marked) {
					var c=t.items[i].classList;
					if(on) c.add('spark'); else c.remove('spark');
				}
			});
		}
		spark();setTimeout(spark, 200);
	},
	open:function(i){
		if(!this.data[i].open&&!this.data[i].marked) {
			if(this.data[i].mine)
				// 点中雷了！
				this.lose(i);
			else {
				// 翻开一个格子
				this.items[i].className='open';
				this.data[i].open=1;this.blanks--;
				if(this.data[i].num)
					// 如果有数字则显示数字
					this.items[i].innerHTML=this.data[i].num;
				else
					// 否则翻开周围的格子
					this.spread(i);
				if(!this.blanks) {
					// 剩余空格数为0了，游戏结束
					this.callback('win');
					// 将所有没标出的雷标记出来
					for(i=0;i<this.total;i++)
						if(this.data[i].mine&&!this.data[i].marked)
							this.items[i].classList.add('mine');
					this.clearTimer();
					this.callback('mine_left',0);
				}
			}
		}
	},
	lose:function(e){
		if(e>=0)	// 点中雷了
			this.items[e].className='wrong mine';
		this.blanks=0;
		this.callback('lose');
		this.clearTimer();
		for(var i=0;i<this.total;i++)
			if(!this.data[i].mine&&this.data[i].marked)
				// 标错雷了
				this.items[i].className='wrong';
			else if(this.data[i].mine&&!this.data[i].marked)
				// 未标记的雷
				this.items[i].classList.add('mine');
	},
};
